使用 Winsock 通过 TCP 发送数据段

您所在的位置:网站首页 windows socket缓冲区设置 使用 Winsock 通过 TCP 发送数据段

使用 Winsock 通过 TCP 发送数据段

2023-07-31 12:34| 来源: 网络整理| 查看: 265

设计问题 - 使用 Winsock 通过 TCP 发送小数据段 项目 07/17/2023

当需要通过 TCP 发送小型数据包时,Winsock 应用程序的设计尤其重要。 不考虑延迟确认、Nagle 算法和 Winsock 缓冲的交互的设计可能会极大地影响性能。 本文通过使用几个案例研究来讨论这些问题。 它还派生一系列建议,以便从 Winsock 应用程序高效发送小型数据包。

原始产品版本: Winsock 原始 KB 编号: 214397

背景

当 Microsoft TCP 堆栈收到数据包时,200 毫秒的延迟计时器将关闭。 发送 ACK 时,将重置延迟计时器,并在收到下一个数据包时再启动 200 毫秒的延迟。 为了提高 Internet 和 Intranet 应用程序的效率,TCP 堆栈使用以下条件决定何时在收到的数据包上发送一个 ACK:

如果在延迟计时器过期之前收到第二个数据包,则会发送 ACK。 如果在收到第二个数据包并延迟计时器过期之前,将以与 ACK 相同的方向发送数据,则 ACK 会使用数据段进行回溯并立即发送。 当延迟计时器过期时,将发送 ACK。

为了避免小数据包充血网络,TCP 堆栈默认启用 Nagle 算法,该算法从多个发送调用中合并一个小数据缓冲区,并延迟发送数据,直到从远程主机接收以前发送的数据包的 ACK。 下面是 Nagle 算法的两个例外:

如果堆栈合并的数据缓冲区大于最大传输单元 (MTU) ,则会立即发送一个全尺寸的数据包,而无需等待远程主机的 ACK。 在以太网网络上,TCP/IP 的 MTU 为 1460 字节。

套 TCP_NODELAY 接字选项用于禁用 Nagle 算法,以便将小型数据包毫不延迟地传递到远程主机。

为了优化应用程序层的性能,Winsock 将数据缓冲区从应用程序复制到 Winsock 内核缓冲区。 然后,堆栈使用自身的启发式 ((如 Nagle 算法)) 来确定何时将数据包实际放在电线上。 可以使用 SO_SNDBUF 选项更改分配给套接字的 Winsock 内核缓冲区量, (默认情况下为 8K,) 。 如有必要,Winsock 可以缓冲大于 SO_SNDBUF 缓冲区大小。 在大多数情况下,应用程序中的发送完成仅指示应用程序发送调用中的数据缓冲区复制到 Winsock 内核缓冲区,并且不指示数据已命中网络介质。 唯一的例外是通过设置为 SO_SNDBUF 0 禁用 Winsock 缓冲时。

Winsock 使用以下规则指示应用程序 (的发送完成情况,具体取决于如何调用发送,完成通知可能是从阻止调用返回的函数、发出事件信号或调用通知函数,等等) :

如果套接字仍处于SO_SNDBUF配额内,Winsock 将从应用程序发送复制数据,并指示向应用程序发送完成。

如果套接字超出 SO_SNDBUF 配额,并且堆栈内核缓冲区中仍然只有一个以前缓冲的发送,Winsock 将复制应用程序发送的数据,并指示向应用程序发送完成的完成。

如果套接字超出 SO_SNDBUF 配额,并且堆栈内核缓冲区中存在多个以前缓冲的发送,Winsock 将复制应用程序发送的数据。 Winsock 不会指示应用程序的发送完成,直到堆栈完成足够的发送,以在配额内 SO_SNDBUF 或只有一个未完成的发送条件中放回套接字。

案例研究 1

Winsock TCP 客户端需要将 10000 条记录发送到 Winsock TCP 服务器才能存储在数据库中。 记录的大小从 20 字节到 100 字节长不等。 为了简化应用程序逻辑,设计如下:

客户端只阻止发送。 服务器仅阻止 recv 。 客户端套接字将设置 SO_SNDBUF 为 0,以便每个记录在单个数据段中出现。 服务器在循环中调用 recv 。 张贴在 recv 其中的缓冲区为 200 字节,以便可以在一次 recv 调用中接收每个记录。 性能

在测试期间,开发人员发现客户端每秒只能向服务器发送五条记录。 总共 10000 条记录(最大为 976 kb 的数据 (10000 * 100 / 1024) ,发送到服务器需要超过半小时的时间。

分析

由于客户端未设置该 TCP_NODELAY 选项,因此 Nagle 算法强制 TCP 堆栈等待 ACK,然后才能在网络上发送另一个数据包。 但是,客户端通过将选项设置为 SO_SNDBUF 0 来禁用 Winsock 缓冲。 因此,必须单独发送 10000 个发送呼叫和 ACK。 每个 ACK 延迟 200 毫秒,因为服务器的 TCP 堆栈上会出现以下情况:

当服务器获取数据包时,其 200 毫秒延迟计时器将关闭。 服务器不需要将任何内容发送回,因此无法回退 ACK。 除非已确认上一个数据包,否则客户端不会发送其他数据包。 服务器上的延迟计时器过期,ACK 将发送回。 如何改进

此设计有两个问题。 首先,存在延迟计时器问题。 客户端需要能够在 200 毫秒内将两个数据包发送到服务器。 由于客户端默认使用 Nagle 算法,因此应只使用默认 Winsock 缓冲,而不设置为 SO_SNDBUF 0。 TCP 堆栈将大于最大传输单元 (MTU) 的缓冲区合并后,会立即发送一个全尺寸的数据包,而无需等待远程主机的 ACK。

其次,此设计为如此小的每个记录调用一个发送。 发送此小尺寸并不有效。 在这种情况下,开发人员可能希望将每个记录填充到 100 字节,并一次从一个客户端发送呼叫发送 80 条记录。 若要让服务器知道将总共发送多少条记录,客户端可能希望启动与包含要遵循的记录数的固定大小标头的通信。

案例研究 2

Winsock TCP 客户端应用程序通过提供股票报价服务的 Winsock TCP 服务器应用程序打开两个连接。 第一个连接用作命令通道,用于将库存符号发送到服务器。 第二个连接用作数据通道来接收股票报价。 建立两个连接后,客户端通过命令通道将股票符号发送到服务器,并等待股票报价通过数据通道返回。 它仅在收到第一个股票报价后才向服务器发送下一个股票符号请求。 客户端和服务器不设置和SO_SNDBUFTCP_NODELAY选项。

性能

在测试期间,开发人员发现客户端每秒只能获取 5 个引号。

分析

此设计一次只允许一个未完成的股票报价请求。 第一个股票符号通过命令通道发送到服务器, (连接) ,响应会通过数据通道 (连接) 立即从服务器发送回客户端。 然后,客户端会立即发送第二个股票符号请求,当发送调用中的请求缓冲区复制到 Winsock 内核缓冲区时,发送将立即返回。 但是,客户端 TCP 堆栈无法立即从其内核缓冲区发送请求,因为尚未确认通过命令通道进行的第一次发送。 服务器命令通道上的 200 毫秒延迟计时器过期后,第一个符号请求的 ACK 将返回到客户端。 然后,第二个引号请求在延迟 200 毫秒后成功发送到服务器。 第二个股票符号的引号立即通过数据通道返回,因为此时客户端数据通道的延迟计时器已过期。 服务器会收到上一引号响应的 ACK。 (请记住,客户端无法为 200 毫秒发送第二个股票报价请求,从而给客户端上的延迟计时器过期并向服务器发送 ACK 的时间。) 因此,客户端获取第二个报价响应,并可以发出另一个引号请求,该请求受同一周期的约束。

如何改进

此处不需要两个连接 (通道) 设计。 如果只对股票报价请求和响应使用一个连接,则报价请求的 ACK 可以回溯到引号响应并立即返回。 为了进一步提高性能,客户端可以将多个股票报价请求 成 一个发送到服务器的调用,服务器还可以将多个引号响应 的倍数转换 为一个发送给客户端的调用。 如果出于某种原因需要进行两个单向通道设计,则双方应设置 TCP_NODELAY 选项,以便可以立即发送小数据包,而无需等待上一个数据包的 ACK。

建议

虽然这两个案例研究是捏造的,但它们有助于说明一些最坏的情况。 设计涉及大量小型数据段发送 recvs的应用程序时,应考虑以下准则:

如果数据段不是时间关键段,则应用程序应将其合并到更大的数据块中,以传递给发送调用。 由于发送缓冲区可能会复制到 Winsock 内核缓冲区,因此缓冲区不应太大。 略小于 8K 是有效的。 只要 Winsock 内核得到大于 MTU 的块,就会发送多个全尺寸数据包和最后一个数据包,并保留任何内容。 发送端(最后一个数据包除外)不会被 200 毫秒延迟计时器击中。 最后一个数据包(如果恰好是奇数数据包)仍受延迟确认算法的约束。 如果发送端堆栈得到另一个大于 MTU 的块,它仍然可以绕过 Nagle 算法。

如果可能,请避免使用单向数据流进行套接字连接。 通过单向套接字进行的通信更容易受到 Nagle 和延迟确认算法的影响。 如果通信遵循请求和响应流,则应使用单个套接字来同时执行发送, recvs 以便 ACK 可以回溯到响应。

如果必须立即发送所有小型数据段,请在发送端设置 TCP_NODELAY 选项。

除非你想要保证在 Winsock 指示发送完成时在网络上发送数据包,否则不应将数据包设置为 SO_SNDBUF 零。 事实上,默认的 8K 缓冲区已在启发式上确定适用于大多数情况,除非测试了新的 Winsock 缓冲区设置是否比默认设置提供更好的性能,否则不应更改它。 此外,设置 SO_SNDBUF 为零对执行批量数据传输的应用程序大有裨益。 即便如此,为了获得最大效率,应将其与双缓冲结合使用 (多个未完成的发送在任何给定时间) 并重叠 I/O。

如果不需要保证数据传送,请使用 UDP。

参考

有关延迟确认和 Nagle 算法的详细信息,请参阅以下内容:

Braden, R.[1989], RFC 1122, Internet 主机的要求 - 通信层, Internet 工程工作队。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3